 /*******************************************************************************
  * Copyright (c) 2003, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.navigator.actions;

 import java.util.Collections ;
 import java.util.Comparator ;
 import java.util.Iterator ;
 import java.util.LinkedHashSet ;
 import java.util.Set ;
 import java.util.TreeSet ;

 import org.eclipse.core.expressions.ElementHandler;
 import org.eclipse.core.expressions.EvaluationContext;
 import org.eclipse.core.expressions.EvaluationResult;
 import org.eclipse.core.expressions.Expression;
 import org.eclipse.core.expressions.ExpressionConverter;
 import org.eclipse.core.expressions.IEvaluationContext;
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.ui.internal.navigator.CustomAndExpression;
 import org.eclipse.ui.internal.navigator.NavigatorPlugin;
 import org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtPtConstants;
 import org.eclipse.ui.internal.navigator.extensions.SkeletonActionProvider;
 import org.eclipse.ui.navigator.CommonActionProvider;
 import org.eclipse.ui.navigator.Priority;

 /**
  *
  *
  * @since 3.2
  */
 public class CommonActionProviderDescriptor implements
         INavigatorContentExtPtConstants {

     private static final String DEFAULT_ID = "org.eclipse.ui.navigator.actionProvider"; //$NON-NLS-1$

     private static int count = 0;

     private final IConfigurationElement configurationElement;

     private final boolean isNested;

     private Set dependentDescriptors;

     private Set overridingDescriptors;

     private IConfigurationElement enablementElement;

     private Expression enablement;

     private boolean hasLoadingFailed;

     private String definedId;

     private String visibilityId;

     private String dependsOnId;

     private String overridesId;

     private String toString;

     private Priority priority;

     /**
      * @param aConfigElement
      * A configuration element with the name "actionProvider" and a
      * "class" attribute which subclasses
      * {@link CommonActionProvider}.
      */
     public CommonActionProviderDescriptor(IConfigurationElement aConfigElement) {
         super();
         Assert.isTrue(TAG_ACTION_PROVIDER.equals(aConfigElement.getName()));
         configurationElement = aConfigElement;
         isNested = false;
         init();
     }

     /**
      * @param aConfigElement
      * A configuration element with the name "actionProvider" and a
      * "class" attribute which subclasses
      * {@link CommonActionProvider}.
      * @param anEnablementExpression
      * A configuration element with the name 'enablement' or
      * 'triggerPoints' and containing an Eclipse Core Expression
      * @param defaultPriority
      * @param anOverrideId
      * A unique identifier for this descriptor. Ids can be used as a
      * filtering device for activities or viewer***Bindings.
      * @param nestedUnderNavigatorContent
      * A value of <b>true</b> indicates that this
      * CommonActionProvider was declared as a nested
      * &lt;actionProvider /&gt; element under a &lt;navigatorContent
      * /&gt; element.
      */
     public CommonActionProviderDescriptor(IConfigurationElement aConfigElement,
             IConfigurationElement anEnablementExpression, Priority defaultPriority, String anOverrideId,
             boolean nestedUnderNavigatorContent) {
         super();
         Assert.isTrue(TAG_ACTION_PROVIDER.equals(aConfigElement.getName()));
         Assert.isTrue(TAG_POSSIBLE_CHILDREN.equals(anEnablementExpression
                 .getName())
                 || TAG_ENABLEMENT.equals(anEnablementExpression.getName()));
         configurationElement = aConfigElement;
         enablementElement = anEnablementExpression;
         visibilityId = anOverrideId;
         isNested = nestedUnderNavigatorContent;
         priority = defaultPriority;
         init();
     }

     private void init() {

         try {

             definedId = configurationElement.getAttribute(ATT_ID);

             // if there was no id attribute, use the default id.
 if (definedId == null) {
                 definedId = DEFAULT_ID + "." + count++; //$NON-NLS-1$
 }

             // we try the id attribute if no override id was supplied.
 if (visibilityId == null) {
                 visibilityId = definedId;
             }

             dependsOnId = configurationElement.getAttribute(ATT_DEPENDS_ON);

             overridesId = configurationElement.getAttribute(ATT_OVERRIDES);
             
             if(priority == null) {
                 String prio = configurationElement.getAttribute(ATT_PRIORITY);
                 if(prio != null)
                     priority = Priority.get(prio);
                 else
                     priority = Priority.NORMAL;
             }

             IConfigurationElement[] children = configurationElement
                     .getChildren(TAG_ENABLEMENT);
             // if no child enablement is specified, and we have an override, use
 // it
 if (children.length == 0 && enablementElement != null) {
                 enablement = new CustomAndExpression(enablementElement);
                 // otherwise the child enablement takes priority
 } else if (children.length == 1) {
                 enablement = ElementHandler.getDefault().create(
                         ExpressionConverter.getDefault(), children[0]);

             } else {
                 System.err.println("Incorrect number of expressions: " + //$NON-NLS-1$
 TAG_ENABLEMENT
                         + " in navigator extension: " + //$NON-NLS-1$
 configurationElement.getDeclaringExtension()
                                 .getUniqueIdentifier() + " in plugin " + //$NON-NLS-1$
 configurationElement.getDeclaringExtension().getNamespaceIdentifier());
             }
         } catch (CoreException e) {
             NavigatorPlugin.log(IStatus.ERROR, 0, e.getMessage(), e);
         }
     }

     /**
      *
      * @return The instantiated CommonActionProvider for this descriptor as
      * declared in the ATT_CLASS attribute or
      * {@link SkeletonActionProvider} if a problem occurs while loading
      * the instance.
      */
     public CommonActionProvider createActionProvider() {
         if (hasLoadingFailed) {
             return SkeletonActionProvider.INSTANCE;
         }
         CommonActionProvider provider = null;
         try {
             provider = (CommonActionProvider) configurationElement
                     .createExecutableExtension(ATT_CLASS);
         } catch (CoreException exception) {
             NavigatorPlugin.log(exception.getStatus());
             hasLoadingFailed = true;
             provider = SkeletonActionProvider.INSTANCE;
         } catch (Exception e) {
             NavigatorPlugin.log(new Status(IStatus.ERROR,
                     NavigatorPlugin.PLUGIN_ID, 0, e.getMessage(), e));
             hasLoadingFailed = true;
             provider = SkeletonActionProvider.INSTANCE;
         }

         return provider;
     }

     /**
      * Determine if this content extension is enabled for the given selection.
      * The content extension is enabled for the selection if and only if it is
      * enabled for each element in the selection.
      *
      * @param aStructuredSelection
      * The selection from the viewer
      * @return True if and only if the extension is enabled for each element in
      * the selection.
      */
     public boolean isEnabledFor(IStructuredSelection aStructuredSelection) {
         if (enablement == null) {
             return false;
         }
         
         if(aStructuredSelection.isEmpty()) {
             IEvaluationContext context = null;
             context = new EvaluationContext(null, Collections.EMPTY_LIST);
             context.setAllowPluginActivation(true);
             try {
                 if (enablement.evaluate(context) != EvaluationResult.TRUE) {
                     return false;
                 }
             } catch (CoreException e) {
                 NavigatorPlugin.log(IStatus.ERROR, 0, e.getMessage(), e);
                 return false;
             }
         } else {

             IEvaluationContext context = null;
             Iterator elements = aStructuredSelection.iterator();
             while (elements.hasNext()) {
                 context = new EvaluationContext(null, elements.next());
                 context.setAllowPluginActivation(true);
                 try {
                     if (enablement.evaluate(context) != EvaluationResult.TRUE) {
                         return false;
                     }
                 } catch (CoreException e) {
                     NavigatorPlugin.log(IStatus.ERROR, 0, e.getMessage(), e);
                     return false;
                 }
             }
         }
         return true;
     }

     /**
      * Determine if this content extension is enabled for the given element.
      *
      * @param anElement
      * The element that should be used for the evaluation.
      * @return True if and only if the extension is enabled for the element.
      */
     public boolean isEnabledFor(Object anElement) {
         if (enablement == null || anElement == null) {
             return false;
         }

         try {
             EvaluationContext context = new EvaluationContext(null, anElement);
             context.setAllowPluginActivation(true);
             return (enablement.evaluate(context) == EvaluationResult.TRUE);
         } catch (CoreException e) {
             NavigatorPlugin.log(IStatus.ERROR, 0, e.getMessage(), e);
         }
         return false;
     }

     /**
      *
      * @return An identifier for this ICommonActionProvider. Defaults to
      * "org.eclipse.ui.navigator.actionProvider". May not be unique.
      * Used to filter the actionProvider using the visibility state
      * information.
      */
     public String getId() {
         return visibilityId;
     }

     /**
      *
      * @return An identifier for this ICommonActionProvider. Defaults to
      * 'org.eclipse.ui.navigator.actionProvider'. May not be unique.
      * Used to determine override or depends on cases.
      */
     public String getDefinedId() {
         return definedId;
     }

     /**
      *
      * @return True if this is a nested &lt;actionProvider /&gt; element.
      */
     public boolean isNested() {
         return isNested;
     }

     /**
      *
      * @return The value specified by the <i>dependsOn</i> attribute of the
      * &lt;actionProvider /&gt; element.
      */
     public String getDependsOnId() {
         return dependsOnId;
     }

     /**
      *
      * @return The value specified by the <i>overridesId</i> attribute of the
      * &lt;actionProvider /&gt; element.
      */
     public String getOverridesId() {
         return overridesId;
     }
     
     /**
      * Only nested Action Providers have priority (as of 3.2.1).
      *
      * @return The priority associated with this Action Provider Descriptor.
      */
     public Priority getPriority() {
         return priority;
     }


     public int hashCode() {
         final int PRIME = 31;
         int result = 1;
         result = PRIME * result + ((definedId == null) ? 0 : definedId.hashCode());
         result = PRIME * result + ((visibilityId == null) ? 0 : visibilityId.hashCode());
         return result;
     }

     public boolean equals(Object obj) {
         if (this == obj)
             return true;
         if (!super.equals(obj))
             return false;
         if (getClass() != obj.getClass())
             return false;
         final CommonActionProviderDescriptor other = (CommonActionProviderDescriptor) obj;
         if (definedId == null) {
             if (other.definedId != null)
                 return false;
         } else if (!definedId.equals(other.definedId))
             return false;
         if (visibilityId == null) {
             if (other.visibilityId != null)
                 return false;
         } else if (!visibilityId.equals(other.visibilityId))
             return false;
         return true;
     }
     
      

     protected void addDependentDescriptor(
             CommonActionProviderDescriptor dependentDescriptor) {
         Assert.isTrue(this != dependentDescriptor);
         if (dependentDescriptors == null) {
             dependentDescriptors = new LinkedHashSet ();
         }
         dependentDescriptors.add(dependentDescriptor);
     }

     protected void addOverridingDescriptor(
             CommonActionProviderDescriptor overridingDescriptor) {
         Assert.isTrue(this != overridingDescriptor);
         if (overridingDescriptors == null) {
             overridingDescriptors = new TreeSet (CommonActionProviderDescriptorCompator.INSTANCE);
         }
         overridingDescriptors.add(overridingDescriptor);
     }

     protected boolean hasDependentDescriptors() {
         return dependentDescriptors != null && !dependentDescriptors.isEmpty();
     }

     protected boolean hasOverridingDescriptors() {
         return overridingDescriptors != null
                 && !overridingDescriptors.isEmpty();
     }

     protected Iterator dependentDescriptors() {
         return dependentDescriptors.iterator();
     }

     protected Iterator overridingDescriptors() {
         return overridingDescriptors.iterator();
     }

     public String toString() {
         if (toString == null) {
             toString = "CommonActionProviderDescriptor[definedId=" + getDefinedId() + ", visibilityId=" + getId() + ", dependsOn=" + getDependsOnId() + ", overrides=" + getOverridesId() + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
 }
         return toString;
     }

     
     /**
      * Sorts CommonActionProviderDescriptors by priority, and then by defined id.
      * @since 3.2
      *
      */
     public static class CommonActionProviderDescriptorCompator implements Comparator {

         /**
          * The singleton instance.
          */
         public static final CommonActionProviderDescriptorCompator INSTANCE = new CommonActionProviderDescriptorCompator();
         
         private static final int LESS_THAN = -1;
         private static final int EQUALS = 0;
         
         /* (non-Javadoc)
          * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
          */
         public int compare(Object o1, Object o2) {
             CommonActionProviderDescriptor lvalue= null, rvalue= null;
             
             if(o1 instanceof CommonActionProviderDescriptor)
                 lvalue = (CommonActionProviderDescriptor) o1;
             
             if(o2 instanceof CommonActionProviderDescriptor)
                 rvalue = (CommonActionProviderDescriptor) o2;
             
             if(lvalue == null || rvalue == null)
                 return LESS_THAN;
             if(lvalue.equals(rvalue))
                 return EQUALS;
             int comparison = lvalue.getPriority().getValue() - rvalue.getPriority().getValue();
             if(comparison == 0)
                 return lvalue.getDefinedId().compareTo(rvalue.getDefinedId());
             return comparison;
             
         }
         
          
     }

 }
  

